package com.jfs.security.filter;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.jfs.constant.CommonConstant;
import com.jfs.entity.User;
import com.jfs.exception.*;
import com.jfs.result.ResultCode;
import com.jfs.security.authentication.JwtAuthenticationToken;
import com.jfs.security.authentication.SessionManager;
import com.jfs.security.properties.SecurityProperties;
import com.jfs.security.service.UserService;
import com.jfs.security.utils.JwtTokenUtils;
import lombok.AllArgsConstructor;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Objects;

/**
 * @Description token检查过滤器
 * @E-mail cwm3412638@163.com
 * @Author cwm
 * @Date 2022/8/5
 */
@Component
@AllArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    private final SecurityProperties securityProperties;
    private final AntPathMatcher antPathMatcher = new AntPathMatcher();
    private final UserService userService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //1.忽略不校验url
        boolean flag = false;
        List<String> ignoreUrls = securityProperties.getIgnoreUrls();
        for (String skpUrl : ignoreUrls
        ) {
            String requestURI = request.getRequestURI();
            flag = antPathMatcher.match(skpUrl, requestURI);
            if (flag) {
                break;
            }
        }
        if (flag) {
            filterChain.doFilter(request, response);
        } else {
            //2.校验token有效性和正确性
            checkToken(request, response, filterChain);
        }
    }

    /**
     * 校验token
     *
     * @param request
     * @param response
     * @param filterChain
     * @throws IOException
     * @throws ServletException
     */
    private void checkToken(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        boolean flag = true;
        String tokenForRequest = JwtTokenUtils.getTokenForRequest();
        //判断是否携带token
        if (StrUtil.isEmpty(tokenForRequest)) {
            request.setAttribute(CommonConstant.ERROR, new NotTokenAuthenticationException(ResultCode.ACCESS_UNAUTHORIZED.getMessage(), ResultCode.ACCESS_UNAUTHORIZED.getCode()));
            request.getRequestDispatcher(CommonConstant.ERROR_DISPATCHE).forward(request, response);
            flag = false;
        }
        //判断token是否失效
        if (JwtTokenUtils.isTokenExpired(tokenForRequest, securityProperties.getSecret())||Objects.isNull(SessionManager.getSession(JwtTokenUtils.getUsernameFromToken(tokenForRequest,securityProperties.getSecret())))) {
            if (flag){
                request.setAttribute(CommonConstant.ERROR, new InvalidTokenAuthenticationException(ResultCode.TOKEN_INVALID_OR_EXPIRED.getMessage(), ResultCode.TOKEN_INVALID_OR_EXPIRED.getCode()));
                request.getRequestDispatcher(CommonConstant.ERROR_DISPATCHE).forward(request, response);
                flag = false;
            }
        }
        //判断用户是否存在
        User user = this.userService.getOne(Wrappers.<User>lambdaQuery().eq(User::getUsername, JwtTokenUtils.getUsernameFromToken(tokenForRequest, securityProperties.getSecret())));
        if (Objects.isNull(user)) {
           if (flag){
               request.setAttribute(CommonConstant.ERROR, new UserNotExistException(ResultCode.USER_NOT_EXIST.getMessage(), ResultCode.USER_NOT_EXIST.getCode()));
               request.getRequestDispatcher(CommonConstant.ERROR_DISPATCHE).forward(request, response);
               flag = false;
           }
        }
        //判断用户是否被禁用
        if (Objects.nonNull(user)){
            if (!user.getEnable()) {
                if (flag){
                    request.setAttribute(CommonConstant.ERROR, new UserAccountDisableException(ResultCode.USER_ACCOUNT_DISABLED.getMessage(), ResultCode.USER_ACCOUNT_DISABLED.getCode()));
                    request.getRequestDispatcher(CommonConstant.ERROR_DISPATCHE).forward(request, response);
                    flag = false;
                }
            }
        }
        //判断是否已经登录了 让上一次登录失效
        Boolean isLogin = SessionManager.getIsLogin(tokenForRequest);
        if (Objects.nonNull(isLogin)) {
            if (!Boolean.TRUE.equals(isLogin)) {
               if (flag){
                   request.setAttribute(CommonConstant.ERROR, new IsLoginTokenAuthenticationException(ResultCode.TOKEN_IS_LOGIN.getMessage(), ResultCode.TOKEN_IS_LOGIN.getCode()));
                   request.getRequestDispatcher(CommonConstant.ERROR_DISPATCHE).forward(request, response);
                   flag = false;
               }
            }
        }
        if (flag) {
            //是否认证通过
            JwtAuthenticationToken jwtAuthenticationToken = new JwtAuthenticationToken(JwtTokenUtils.getUsernameFromToken(tokenForRequest, securityProperties.getSecret()), null, true);
            SecurityContextHolder.getContext().setAuthentication(jwtAuthenticationToken);
        }
        filterChain.doFilter(request, response);
    }
}
